package com.amc.framework.aspectj;

import com.amc.common.annotation.CustomCache;
import com.amc.framework.redis.RedisCache;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.TimeUnit;

@Aspect
@Component
public class CustomCacheAspect {
    @Resource
    private RedissonClient redissonClient;

    @Resource
    private RedisCache redisCache;

    @Around(value = "@annotation(customCache)")
    public Object cacheAround(ProceedingJoinPoint pjp, CustomCache customCache) {
        //签名：  包名+类名 +方法名
        MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
        Method method = methodSignature.getMethod();
        String prefix = customCache.prefix();
        String suffix = customCache.suffix();
        long timeout = customCache.timeout();
        TimeUnit timeUnit = customCache.timeUnit();
        //获取形参
        Object[] args = pjp.getArgs();
        //  sku:[skuId,spuId,age,sex,birthday]:info
        //缓存的Key值
        String cacheKey = prefix + ":" + Arrays.asList(args) + suffix;
        //1:查询缓存
        Object o = redisCache.getCacheObject(cacheKey);
        if (null == o) {
            //1.1 缓存击穿
            //String cacheKeyLock = prefix + ":" + Arrays.asList(args) + suffix;
            String cacheKeyLock = prefix + ":" + args.toString() + suffix;
            RLock lock = redissonClient.getLock(cacheKeyLock);
            try {
                boolean isLock = lock.tryLock(2, 3, TimeUnit.SECONDS);
                if (isLock) {
                    //获取到锁
                    //2:没有 再查询DB
                    try {
                        Object result = pjp.proceed();//进入方法中
                        if (null != result) {
                            //3:将查询的数据保存到缓存中一份  防止缓存雪崩问题 过期时间差
                            redisCache.setCacheObject(cacheKey, result, timeout + new Random().nextInt(300), timeUnit);
                            //4:直接返回
                            o = result;
                        } else {
                            //1.2 缓存穿透
                            result = methodSignature.getReturnType().newInstance();
                            redisCache.setCacheObject(cacheKey, result, 5, TimeUnit.MINUTES);
                        }
                    } finally {
                        //解锁
                        lock.unlock();
                    }
                } else {
                    //未获取到锁
                    TimeUnit.SECONDS.sleep(3);
                    return redisCache.getCacheObject(cacheKey);
                }
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
        //4:直接返回
        return o;
    }
}

